package server.proxy;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;

import server.proxy.db.WhiteList;
import server.proxy.factory.ProxySingletoneFactory;

import files.handlers.FileHandler;

/**
 * this class enforce the required policy of sending X packets in Y minutes.
 * 
 * @author Maxim
 *
 */
public class ProxyServerRequestCounter extends SimpleChannelUpstreamHandler {
	
	private RequestCounter _requestCounter;
	private WhiteList _whiteList;
	
	public ProxyServerRequestCounter() throws InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException, InvalidKeySpecException, FileNotFoundException, IOException{
		
		this._whiteList = ProxySingletoneFactory.getWhiteListSingletone();
		this._requestCounter = ProxySingletoneFactory.getRequestCounterSingletone();
		
	}
	
	@Override
    public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
        // Add all open channels to the global group so that they are
        // closed on shutdown.
       
    	ProxyServer.allChannels.add(e.getChannel());
    }
	
	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception{
		
		
		
		String tClientIP = parseSenderIP(e.getChannel().getRemoteAddress().toString());
		
		if(this._requestCounter.canSendMoreRequest(tClientIP)){
			
			this._requestCounter.addRequest(tClientIP, System.currentTimeMillis());
			ctx.sendUpstream(e);
		}
		else{
			
			//remove this client from the white list
			this._whiteList.deleteClientIP(tClientIP);
			e.getChannel().write(createResponse(Config.BLOCKED_MESSAGE_HTML_FILE_HAME));
		}
		
		
	}
	
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
		
		System.out.println("Unexpected exception from proxy server request counter: " + e.getCause());
		e.getChannel().close();
	}
	
	/**
	 * This method create HTTP response packet with the given message
	 * @param message
	 * @return
	 * @throws IOException
	 * 
	 */
	@SuppressWarnings("deprecation")
	private HttpResponse createResponse(String message) throws IOException{
	 	HttpResponse tResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
	 	tResponse.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=utf-8");
     
	 	ChannelBuffer tBuf = ChannelBuffers.copiedBuffer(FileHandler.readFileAsString(message), "utf-8");
     	tResponse.setContent(tBuf);
     	tResponse.setHeader("Content-Length", String.valueOf(tBuf.readableBytes()));
     
     	return tResponse;
	}
	
	
	/**
	 * 
	 * This method extracts the sender IP from remote address
	 * @param senderIP remote address
	 * @return String representation in form *.*.*.*
	 * 
	 */
	private String parseSenderIP(String senderIP){
		
		String tAnswer = ""; 
		String tDigits = "0123456789";
		byte [] tSenderIP = senderIP.getBytes(Charset.defaultCharset());
		int tSenderIPLength = tSenderIP.length;
		
		for(int j = 0; j < tSenderIPLength; j++){
			if((char)tSenderIP[j]== ':')
				break;
			
			if((char)tSenderIP[j]== '.'  | tDigits.indexOf((char)tSenderIP[j]) != -1 )
				tAnswer = tAnswer.concat(String.valueOf((char)tSenderIP[j]));
		}
		return tAnswer;
	}

}
